﻿using System;
using System.Collections.Generic;
using System.Linq;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// Implements an undoable command for creating a new music section.
    /// </summary>
    public class NewMusicSectionCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="NewMusicSectionCommand"/> class.
        /// </summary>
        /// <param name="undoStack">The command stack where the command will be put after its execution</param>
        /// <param name="redoStack">The command stack where the command will be put when it has been undone</param>
        public NewMusicSectionCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<Elements.Composition>("Composition", ParameterDirection.In);
            AddParameter<ushort>("FirstMeasure", ParameterDirection.In);
            AddParameter<Model.Elements.SectionInteriorCreator>("InteriorCreator", ParameterDirection.In);
            AddParameter<ushort>("LastMeasure", ParameterDirection.In);
            AddParameter<Elements.Page>("Owner", ParameterDirection.In);
            AddParameter<object>("Parameter", ParameterDirection.In);
            AddParameter<ICollection<Elements.PartDescriptor>>("PresentedParts", ParameterDirection.In);

            AddParameter<Elements.MusicSection>("CreatedSection", ParameterDirection.Out);

            InteriorCreator = Model.Elements.CWNotation.MusicSectionInterior.Create;
            Parameter = null;
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            var composition = (Model.Elements.CompositionElement)Composition.AdaptedElement;

            var modelSection = ((Model.Elements.PageElement)Owner.AdaptedElement).CreateMusicSection(
                Composition.AdaptedPart as Model.Packaging.CompositionPart,
                composition[FirstMeasure],
                composition[LastMeasure],
                InteriorCreator
                );
            
            if (PresentedParts != null)
            {
                foreach (var part in PresentedParts)
                    modelSection.Parts.Add(part.AdaptedElement as Model.Elements.PartDescriptorElement);
            }

            modelSection.Interior.CreateContent(Parameter);
            CreatedSection = (Elements.MusicSection)Owner.Sections.First(section => section.AdaptedElement == modelSection);

            Owner.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            ((Model.Elements.PageElement)Owner.AdaptedElement).Sections.Add((Model.Elements.SectionElement)CreatedSection.AdaptedElement);
            var vmSection = Owner.Sections.First(s => s.AdaptedElement == CreatedSection.AdaptedElement);
            Owner.Sections[Owner.Sections.IndexOf(vmSection)] = CreatedSection;

            Owner.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            Owner.ClearSelection();
            ((Model.Elements.PageElement)Owner.AdaptedElement).Sections.Remove((Model.Elements.SectionElement)CreatedSection.AdaptedElement);

            Owner.FindOwner<Elements.Project>().IsModified = true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the resource that contains the image data.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Resource"/>
        /// </para>
        /// <para>
        /// The resource that contains the image data that will
        /// be presented in the created section.<br/>
        /// This property wraps the "Composition" command parameter.
        /// </para>
        /// </value>
        public Elements.Composition Composition
        {
            get { return GetParameterValue<Elements.Composition>("Composition"); }
            set { SetParameterValue<Elements.Composition>("Composition", value); }
        }

        /// <summary>
        /// Gets the created section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.MusicSection"/>
        /// </para>
        /// <para>
        /// The section created by this command.<br/>
        /// This property wraps the "CreatedSection" command parameter.
        /// </para>
        /// </value>
        public Elements.MusicSection CreatedSection
        {
            get { return GetParameterValue<Elements.MusicSection>("CreatedSection"); }
            private set { SetParameterValue<Elements.MusicSection>("CreatedSection", value); }
        }

        /// <summary>
        /// Gets the first measure presented in the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The number of the first measure of the <see cref="Composition"/>
        /// that will be presented in the created section.<br/>
        /// This property wraps the "FirstMeasure" command parameter.
        /// </para>
        /// </value>
        public ushort FirstMeasure
        {
            get { return GetParameterValue<ushort>("FirstMeasure"); }
            set { SetParameterValue<ushort>("FirstMeasure", value); }
        }

        /// <summary>
        /// Gets or sets the method that will create the interior of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Model.Elements.SectionInteriorCreator"/>
        /// </para>
        /// <para>
        /// The method that will create the interior of the section.<br/>
        /// This property wraps the "InteriorCreator" command parameter.
        /// </para>
        /// </value>
        public Model.Elements.SectionInteriorCreator InteriorCreator
        {
            get { return GetParameterValue<Model.Elements.SectionInteriorCreator>("InteriorCreator"); }
            set { SetParameterValue<Model.Elements.SectionInteriorCreator>("InteriorCreator", value); }
        }

        /// <summary>
        /// Gets the last measure presented in the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The number of the last measure of the <see cref="Composition"/>
        /// that will be presented in the created section.<br/>
        /// This property wraps the "LastMeasure" command parameter.
        /// </para>
        /// </value>
        public ushort LastMeasure
        {
            get { return GetParameterValue<ushort>("LastMeasure"); }
            set { SetParameterValue<ushort>("LastMeasure", value); }
        }
        
        /// <summary>
        /// Gets or sets the page that will own the created section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Page"/>
        /// </para>
        /// <para>
        /// The page that will own the created section.<br/>
        /// This property wraps the "Owner" command parameter.
        /// </para>
        /// </value>
        public Elements.Page Owner
        {
            get { return GetParameterValue<Elements.Page>("Owner"); }
            set { SetParameterValue<Elements.Page>("Owner", value); }
        }

        /// <summary>
        /// Gets or sets the initialization parameter for the section interior.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="object"/>
        /// </para>
        /// <para>
        /// The initialization parameter that will be passed to the section interior.
        /// Default value is <see langword="null"/>.<br/>
        /// This property wraps the "Parameter" command parameter.
        /// </para>
        /// </value>
        public object Parameter
        {
            get { return GetParameterValue<object>("Parameter"); }
            set { SetParameterValue<object>("Parameter", value); }
        }

        /// <summary>
        /// Gets or sets the collection of parts presented in the created section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ICollection{T}"/> of <see cref="Elements.PartDescriptor"/>
        /// </para>
        /// <para>
        /// The collection of descriptors of the parts that will be presented
        /// in the created section.<br/>
        /// This property wraps the "PresentedParts" command parameter.
        /// </para>
        /// </value>
        public ICollection<Elements.PartDescriptor> PresentedParts
        {
            get { return GetParameterValue<ICollection<Elements.PartDescriptor>>("PresentedParts"); }
            set { SetParameterValue<ICollection<Elements.PartDescriptor>>("PresentedParts", value); }
        }

        #endregion
    }
}
